package com.ybzl.gateway.utils;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ybzl.gateway.config.EmqxConfig;
import com.ybzl.gateway.domain.DeviceInfo;
import com.ybzl.gateway.domain.NoIbeacon;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;


@Slf4j
@Component
public class NoIbeaconCalc {

    @Autowired
    private RedisTemplate redisTemplate;

    public void getNoIbeacon(List<NoIbeacon> noIbeaconList) {
        Long st1 = System.currentTimeMillis();
        if (noIbeaconList.size() > 0) {
            // 根据标签进行分组
            Map<String, List<NoIbeacon>> noIbeaconExIdMap = noIbeaconList.stream().filter(t -> Objects.nonNull(t)).collect(Collectors.groupingBy(NoIbeacon::getTagId, TreeMap::new, Collectors.toList()));

            List<NoIbeacon> tempList = new ArrayList<>();
            for (String tagId : noIbeaconExIdMap.keySet()) {
                // 对基站进行分组 一个标签对应多个基站
                Map<String, List<NoIbeacon>> exIdMap = noIbeaconExIdMap.get(tagId).stream().collect(Collectors.groupingBy(NoIbeacon::getExId, TreeMap::new, Collectors.toList()));

                for (String exId : exIdMap.keySet()) {
                    // 在同一个标签下对基站进行分组 获取型号强度最强的一个
                    List<NoIbeacon> collect = exIdMap.get(exId).stream().sorted(Comparator.comparing(NoIbeacon::getRssi).reversed()).collect(Collectors.toList());
                    tempList.add(collect.get(0));
                }
            }
            // 最终的集合数据
            Map<String, List<NoIbeacon>> tempListMap = tempList.stream().collect(Collectors.groupingBy(NoIbeacon::getTagId, TreeMap::new, Collectors.toList()));

            //log.info("Result Data Map：{}", JSONObject.toJSONString(tempListMap));
            // 【开始每一个标签和扫描到的基站列表】
            for (String tagId : tempListMap.keySet()) {
                List<NoIbeacon> collect = tempListMap.get(tagId).stream().sorted(Comparator.comparing(NoIbeacon::getRssi).reversed()).collect(Collectors.toList());
                NoIbeacon currentNoIbeacon = collect.get(0);
                String noibeaconStr = (String) redisTemplate.opsForValue().get("noibeacon::tagId::" + tagId);
                if (StringUtils.isEmpty(noibeaconStr)) {
                    // 第一次出现直接推送
                    // 推送当前标签数据
                    EmqxConfig.push("location", false, currentNoIbeacon);
                    EmqxConfig.push("locationWeb", false, currentNoIbeacon);
                    redisTemplate.opsForValue().set("noibeacon::tagId::" + tagId, JSONObject.toJSONString(currentNoIbeacon));
                } else {
                    // 不是第一次出现
                    // 获取上一次的数据
                    NoIbeacon existNoIbecon = JSONObject.parseObject(noibeaconStr, NoIbeacon.class);

                    NoIbeacon a = null;
                    boolean contains = false;
                    for (NoIbeacon t : collect) {
                        if (existNoIbecon.getExId().equals(t.getExId())) {
                            // 本包数据包含上一次位置信息
                            contains = true;
                            a = t;
                            break;
                        }
                    }

                    /***
                     * 1. 本包数据包含上次位置
                     * 1.1 最大rssi所在基站和上次基站一样 （√）
                     * 1.2 最大rssi所在基站和上次基站不一样 （×）
                     *
                     * 2. 本包数据不包含上次位置
                     * 2.1 最大rssi所在基站和上次基站不一样
                     *
                     *
                     */

                    if (contains) {
                        // [1.1]场景
                        if (currentNoIbeacon.getExId().equals(existNoIbecon.getExId())) {
                            EmqxConfig.push("location", false, existNoIbecon);
                            EmqxConfig.push("locationWeb", false, existNoIbecon);
                            redisTemplate.opsForValue().set("noibeacon::tagId::" + tagId, JSONObject.toJSONString(a));
                        }
                    } else {
                        // [2.1]场景
                        // todo 可以考虑追加一个两个基站 rssi 变化的差值
                        log.info("3:=> existNoIbecon:{},currentNoIbeacon:{}, st:{}, ed:{}, fx:{}", existNoIbecon, currentNoIbeacon, buildTiem(existNoIbecon.getTs()), buildTiem(currentNoIbeacon.getTs()), existNoIbecon.getExId() + "->" + currentNoIbeacon.getExId());
                        if (currentNoIbeacon.getTs() - existNoIbecon.getTs() <= 2000) {
                            log.info("4:=>异常指标---> existNoIbecon:{},currentNoIbeacon:{}", existNoIbecon, currentNoIbeacon);
                            return;

                        }
                        EmqxConfig.push("location/change", false, new HashMap<String, DeviceInfo>() {
                            {
                                put("preDeviceInfo", new DeviceInfo(existNoIbecon.getTs(), existNoIbecon.getTagId(), existNoIbecon.getClientIP(), existNoIbecon.getExId(), 1, existNoIbecon.getRssi()));
                                put("currentDeviceInfo", new DeviceInfo(DateUtil.date().getTime(), currentNoIbeacon.getTagId(), currentNoIbeacon.getClientIP(), currentNoIbeacon.getExId(), 1, currentNoIbeacon.getRssi()));
                            }
                        });
                        redisTemplate.opsForValue().set("noibeacon::tagId::" + currentNoIbeacon.getTagId(), JSONObject.toJSONString(currentNoIbeacon));
                    }
                }
            }
        }
        Long st2 = System.currentTimeMillis();
        log.info("Push Date Time：{} 毫秒", st2 - st1);
    }

    /**
     * 基于系统时区实现的日期格式转换
     *
     * @param timestamp
     * @return
     */
    public LocalDateTime buildTiem(Long timestamp) {
        Instant instant = Instant.ofEpochMilli(timestamp);
        // 使用系统默认时区将Instant转换为LocalDateTime
        return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    // 使用ConcurrentMap保证位置状态原子性
    private final ConcurrentMap<String, PositionState> positionStates = new ConcurrentHashMap<>();


    public void processBatch(List<NoIbeacon> batch) {
        // 按标签分组处理
        Map<String, List<NoIbeacon>> tagGroups = batch.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(NoIbeacon::getTagId));

        for (Map.Entry<String, List<NoIbeacon>> entry : tagGroups.entrySet()) {
            processTagData(entry.getKey(), entry.getValue());
        }
    }

    private void processTagData(String tagId, List<NoIbeacon> dataList) {
        // 原子获取位置状态
        PositionState state = positionStates.compute(tagId, (key, existing) -> {
            if (existing == null) {
                // 从Redis加载初始状态
                String redisData = (String) redisTemplate.opsForValue().get("noibeacon::tagId::" + tagId);
                return redisData != null ? new PositionState(JSON.parseObject(redisData, NoIbeacon.class)) : new PositionState();
            }
            return existing;
        });

        // 查找数据包中是否包含上次位置
        Optional<NoIbeacon> lastPositionInBatch = dataList.stream().filter(data -> data.getExId().equals(state.getLastExId())).findFirst();

        if (lastPositionInBatch.isPresent()) {
            // 位置未变化：更新A的强度和时间
            NoIbeacon updatedA = lastPositionInBatch.get();
            state.updatePosition(updatedA);

            EmqxConfig.push("location", false, updatedA);
            EmqxConfig.push("locationWeb", false, updatedA);
            redisTemplate.opsForValue().set("noibeacon::tagId::" + tagId, JSONObject.toJSONString(updatedA));

        } else {
            // 位置变化处理
            handlePositionChange(tagId, state, dataList);
        }
    }

    private void handlePositionChange(String tagId, PositionState state, List<NoIbeacon> dataList) {
        // 选择新位置（信号最强的基站）
        Optional<NoIbeacon> newPositionOpt = dataList.stream().max(Comparator.comparing(NoIbeacon::getRssi));

        if (!newPositionOpt.isPresent()) return;

        NoIbeacon newPosition = newPositionOpt.get();
        NoIbeacon lastPosition = state.getLastPosition();

        // 时间差检查
        if (lastPosition != null && newPosition.getTs() - lastPosition.getTs() <= 2000) {
            log.warn("位置变化过快: {}->{}", lastPosition.getExId(), newPosition.getExId());
            return;
        }

        // 记录位置变化
        log.info("位置变化: {} -> {}", lastPosition != null ? lastPosition.getExId() : "N/A", newPosition.getExId());

        // [2.1]场景
        // todo 可以考虑追加一个两个基站 rssi 变化的差值
        log.info("3:=> existNoIbecon:{},currentNoIbeacon:{}, st:{}, ed:{}, fx:{}", lastPosition, newPosition, buildTiem(lastPosition.getTs()), buildTiem(newPosition.getTs()), lastPosition.getExId() + "->" + newPosition.getExId());
        if (newPosition.getTs() - lastPosition.getTs() <= 2000) {
            log.info("4:=>异常指标---> existNoIbecon:{},currentNoIbeacon:{}", lastPosition, newPosition);
            return;

        }
        EmqxConfig.push("location/change", false, new HashMap<String, DeviceInfo>() {
            {
                put("preDeviceInfo", new DeviceInfo(lastPosition.getTs(), lastPosition.getTagId(), lastPosition.getClientIP(), lastPosition.getExId(), 1, lastPosition.getRssi()));
                put("currentDeviceInfo", new DeviceInfo(DateUtil.date().getTime(), newPosition.getTagId(), newPosition.getClientIP(), newPosition.getExId(), 1, newPosition.getRssi()));
            }
        });

        // 更新位置状态
        state.updatePosition(newPosition);
    }

    // 位置状态内部类
    private static class PositionState {
        private volatile NoIbeacon lastPosition;

        PositionState() {
        }

        PositionState(NoIbeacon position) {
            this.lastPosition = position;
        }

        String getLastExId() {
            return lastPosition != null ? lastPosition.getExId() : null;
        }

        NoIbeacon getLastPosition() {
            return lastPosition;
        }

        void updatePosition(NoIbeacon newPosition) {
            this.lastPosition = newPosition;
        }
    }


}

